/*!
* UX4G Accessibility beta v1.12.0 (https://doc.ux4g.gov.in/)
* Copyright 2025 The UX4G Authors(Vipul Agarwal, Ershad Alam, Suman Prasad)
* Copyright 2025 NeGD, MeitY.
* Licensed under MIT.
*/
(function () {
const SETTINGS_KEY = "accessibilitySettings";
// Function to get cookie domain dynamically
function getCookieDomain() {
const hostname = location.hostname;
// For localhost or IP addresses, return empty string
if (hostname === 'localhost' || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
return '';
}
// For regular domains, prepend with dot for cross-subdomain access
const parts = hostname.split('.');
if (parts.length > 1) {
return '.' + parts.slice(1).join('.'); // ".example.co.in"
}
}
const widgetHTML = `
UX4G Accessibility Tool
`;
document.body.insertAdjacentHTML('beforeend', widgetHTML);
document.addEventListener("DOMContentLoaded", loadSettings);
document.addEventListener("scroll", function () {
detectRouteChange()
})
document.getElementById('uw-widget-custom-trigger').addEventListener('click', function () {
document.getElementById('uw-main').style.right = '0';
});
function closeMain() {
document.getElementById('uw-main').style.right = '-530px';
}
document.addEventListener('DOMContentLoaded', function () {
const closeButtons = document.querySelectorAll('.uwaw-close');
closeButtons.forEach(function(button) {
button.addEventListener('click', closeMain);
});
});
let fontSizeCount = 0;
let lineHeightCount = 0;
let textSpacingCount = 0;
let lastPath = window.location.pathname;
let screenReader = false;
const fontSizeSpans = document.querySelectorAll('#featureSteps span');
const lineHeightSpans = document.querySelectorAll('#featureSteps-lh span');
const textSpacingSpans = document.querySelectorAll('#featureSteps-ts span');
let speechSynthesisInstance = window.speechSynthesis;
let tabPressCount = 0;
// Function to Speak Text
function speakText(text) {
if (!text.trim()) return; // Prevent empty speech
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = "en-US";
utterance.rate = 1;
speechSynthesisInstance.cancel();
speechSynthesisInstance.speak(utterance);
}
// Detect Tab Key Press and Announce Accessibility Menu
document.addEventListener("keydown", (event) => {
if (event.key === "Tab") {
tabPressCount++;
if (tabPressCount === 2) {
speakText("Press Enter to open accessibility menu.");
}
} else if (event.key === "Enter" && tabPressCount === 2) {
speakText("Opening accessibility menu.");
tabPressCount = 0;
}
});
// Reset Tab Count on Mouse Click
document.addEventListener("click", (event) => {
tabPressCount = 0;
let element = event.target;
let clickedText = "";
if (element.tagName === "IMG") {
clickedText = element.getAttribute("alt") || element.getAttribute("aria-label") || element.getAttribute("title") || "Clickable image";
} else {
clickedText = element.innerText.trim();
}
if (clickedText && screenReader) {
speakText(clickedText);
}
});
document.addEventListener("mouseup", () => {
let selectedText = window.getSelection().toString();
if (selectedText && screenReader) {
speakText(selectedText);
}
});
// Speak Element's Name on Hover
document.addEventListener("mouseover", (event) => {
let element = event.target;
let textToSpeak = "";
if (element.tagName === "IMG" && element.closest("A, BUTTON")) {
textToSpeak = element.getAttribute("alt") || element.getAttribute("aria-label") || element.getAttribute("title") || "Clickable image";
} else if (element.tagName === "A" || element.tagName === "BUTTON" || element.tagName === "INPUT" || element.tagName === "TEXTAREA" || element.hasAttribute("role")) {
textToSpeak = element.innerText || element.getAttribute("aria-label") || element.getAttribute("alt") || element.value || "Interactive element";
}
if (textToSpeak && screenReader) {
speechSynthesisInstance.cancel();
speakText(textToSpeak);
}
});
function toggleTextToSpeech() {
const tickIcon = document.getElementById('tickIcon_sp');
const button = document.getElementById('featureItem_sp');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
screenReader = !screenReader;
saveSettings();
if (speechSynthesis.speaking) {
speechSynthesis.cancel();
}
}
document.addEventListener('DOMContentLoaded', function () {
const speakButton = document.getElementById('speak');
if (speakButton) {
speakButton.addEventListener('click', toggleTextToSpeech);
}
});
function updateLineHeight() {
document.querySelectorAll('body *:not(.uwaw *):not(.uwaw)').forEach((el) => {
let currentSize = parseFloat(window.getComputedStyle(el).lineHeight) || parseFloat(window.getComputedStyle(el).fontSize) * 1.2;
el.style.lineHeight = (currentSize + 1) + 'px';
});
}
function updateLetterSpacing() {
document.querySelectorAll('body *:not(.uwaw *):not(.uwaw)').forEach((el) => {
el.style.letterSpacing = (textSpacingCount * .12) + 'em';
el.style.wordSpacing = (.16 * textSpacingCount) + 'em';
});
}
function applyTextSettings() {
let elements = document.querySelectorAll('body > *:not(.uwaw)');
elements.forEach(el => {
let currentZoom = parseFloat(el.style.zoom) || 1;
el.style.zoom = (currentZoom + 0.1).toFixed(2);
});
}
function adjustFontSize() {
const button = document.getElementById('featureItem');
const tickIcon = document.getElementById('tickIcon');
const fontCheck = document.getElementById('featureSteps');
button.classList.add('feature-active');
fontSizeCount = (fontSizeCount + 1) % 5;
applyTextSettings();
saveSettings();
if (fontSizeCount === 0) {
button.classList.remove('feature-active');
tickIcon.style.display = 'none';
fontCheck.classList.remove('featureSteps-visible');
document.querySelectorAll('body > *:not(.uwaw)').forEach(el => el.style.zoom = 1);
fontSizeSpans.forEach(span => span.classList.remove('active'));
} else {
tickIcon.style.display = 'inline-flex';
fontCheck.classList.add('featureSteps-visible');
fontSizeSpans.forEach(span => span.classList.remove('active'));
fontSizeSpans.forEach((span, index) => {
if (index <= fontSizeCount - 1) {
span.classList.add('active');
}
});
}
}
document.addEventListener('DOMContentLoaded', function () {
const fontSizeBtn = document.getElementById('btn-s9');
if (fontSizeBtn) {
fontSizeBtn.addEventListener('click', adjustFontSize);
}
});
function adjustLineHeight() {
const button = document.getElementById('featureItem-lh');
const tickIcon = document.getElementById('tickIcon-lh');
const lineHeightCheck = document.getElementById('featureSteps-lh');
button.classList.add('feature-active');
lineHeightCount = (lineHeightCount + 1) % 5;
updateLineHeight();
saveSettings();
if (lineHeightCount === 0) {
button.classList.remove('feature-active');
tickIcon.style.display = 'none';
lineHeightCheck.classList.remove('featureSteps-visible');
document.querySelectorAll('body *:not(.uwaw *):not(.uwaw)').forEach((el) => {
el.style.lineHeight = "";
});
lineHeightSpans.forEach(span => span.classList.remove('active'));
} else {
tickIcon.style.display = 'inline-flex';
lineHeightSpans.forEach(span => span.classList.remove('active'));
lineHeightSpans.forEach((span, index) => {
if (index <= lineHeightCount - 1) {
span.classList.add('active');
}
});
lineHeightCheck.classList.add('featureSteps-visible');
}
}
document.addEventListener('DOMContentLoaded', function () {
const lineHeightBtn = document.getElementById('btn-s12');
if (lineHeightBtn) {
lineHeightBtn.addEventListener('click', adjustLineHeight);
}
});
function adjustTextSpacing() {
const button = document.getElementById('featureItem-ts');
button.classList.add('feature-active');
textSpacingCount = (textSpacingCount + 1) % 4;
updateLetterSpacing();
saveSettings();
const tickIcon = document.getElementById('tickIcon-ts');
const textSpacingCheck = document.getElementById('featureSteps-ts');
if (textSpacingCount === 0) {
button.classList.remove('feature-active');
tickIcon.style.display = 'none';
textSpacingCheck.classList.remove('featureSteps-visible');
document.querySelectorAll('body *:not(.uwaw *):not(.uwaw)').forEach(el => {
el.style.letterSpacing = "";
});
textSpacingSpans.forEach(span => span.classList.remove('active'));
} else {
tickIcon.style.display = 'inline-flex';
textSpacingSpans.forEach(span => span.classList.remove('active'));
textSpacingSpans.forEach((span, index) => {
if (index <= textSpacingCount - 1) {
span.classList.add('active');
}
});
textSpacingCheck.classList.add('featureSteps-visible');
}
}
document.addEventListener('DOMContentLoaded', function () {
const spacingBtn = document.getElementById('btn-s13');
if (spacingBtn) {
spacingBtn.addEventListener('click', adjustTextSpacing);
}
});
function toggleHighlightLinks() {
const button = document.getElementById('featureItem-ht');
const tickIcon = document.getElementById('tickIcon-ht');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
document.body.classList.toggle('highlight-links');
let tool = document.querySelector('.uwaw');
if (tool) {
tool.classList.remove('highlight-links');
}
saveSettings();
}
document.addEventListener('DOMContentLoaded', function () {
const highlightBtn = document.getElementById('btn-s10');
if (highlightBtn) {
highlightBtn.addEventListener('click', toggleHighlightLinks);
}
});
function toggleDyslexiaMode() {
const button = document.getElementById('featureItem-df');
const tickIcon = document.getElementById('tickIcon-df');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
document.body.classList.toggle('dyslexia-mode');
saveSettings();
}
document.addEventListener('DOMContentLoaded', function () {
const dyslexiaBtn = document.getElementById('btn-df');
if (dyslexiaBtn) {
dyslexiaBtn.addEventListener('click', toggleDyslexiaMode);
}
});
function hideImages() {
const button = document.getElementById('featureItem-hi');
const tickIcon = document.getElementById('tickIcon-hi');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
document.body.classList.toggle('hide-images');
saveSettings();
}
document.addEventListener('DOMContentLoaded', function () {
const hideImagesBtn = document.getElementById('btn-s11');
if (hideImagesBtn) {
hideImagesBtn.addEventListener('click', hideImages);
}
});
function changeCursor() {
const button = document.getElementById('featureItem-Cursor');
const tickIcon = document.getElementById('tickIcon-cursor');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
document.body.classList.toggle('custom-cursor');
saveSettings();
}
document.addEventListener('DOMContentLoaded', function () {
const cursorBtn = document.getElementById('btn-cursor');
if (cursorBtn) {
cursorBtn.addEventListener('click', changeCursor);
}
});
function toggleDarkMode() {
const button = document.getElementById('featureItem-ht-dark');
const tickIcon = document.getElementById('tickIcon-ht-dark');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
document.body.classList.toggle('dark-mode');
saveSettings();
}
document.addEventListener('DOMContentLoaded', function () {
const darkModeBtn = document.getElementById('dark-btn');
if (darkModeBtn) {
darkModeBtn.addEventListener('click', toggleDarkMode);
}
});
function invertColor() {
const button = document.getElementById('featureItem-ic');
const tickIcon = document.getElementById('tickIcon-ic');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
document.documentElement.classList.toggle('invert-colors');
saveSettings();
}
document.addEventListener('DOMContentLoaded', function () {
const invertBtn = document.getElementById('btn-invert');
if (invertBtn) {
invertBtn.addEventListener('click', invertColor);
}
});
function applyADHDFriendlyMode() {
const allTextNodes = document.body.querySelectorAll(
'p:not(.uwaw *), h1:not(.uwaw *), h2:not(.uwaw *), h3:not(.uwaw *), h4:not(.uwaw *), h5:not(.uwaw *), h6:not(.uwaw *), span:not(.uwaw *), li:not(.uwaw *), a:not(.uwaw *)'
);
allTextNodes.forEach(node => {
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim().length > 0) {
let text = node.textContent;
let updatedText = text.replace(/\b([a-zA-Z0-9]+)\b/g, (match) => {
if (/|/.test(match)) return match;
let word = match;
if (word.length % 2 === 0) {
let halfLength = Math.floor(word.length / 2);
return `${word.substring(0, halfLength)}${word.substring(halfLength)}`;
} else {
let halfLength = Math.floor(word.length / 2) + 1;
return `${word.substring(0, halfLength)}${word.substring(halfLength)}`;
}
});
let tempDiv = document.createElement('div');
tempDiv.innerHTML = updatedText;
node.replaceWith(...tempDiv.childNodes);
} else if (node.nodeType === Node.ELEMENT_NODE) {
const childNodes = node.childNodes;
childNodes.forEach(childNode => {
if (childNode.nodeType === Node.TEXT_NODE && childNode.textContent.trim().length > 0) {
let text = childNode.textContent;
let updatedText = text.replace(/\b([a-zA-Z0-9]+)\b/g, (match) => {
if (/|/.test(match)) return match;
let word = match;
if (word.length % 2 === 0) {
let halfLength = Math.floor(word.length / 2);
return `${word.substring(0, halfLength)}${word.substring(halfLength)}`;
} else {
let halfLength = Math.floor(word.length / 2) + 1;
return `${word.substring(0, halfLength)}${word.substring(halfLength)}`;
}
});
let tempDiv = document.createElement('div');
tempDiv.innerHTML = updatedText;
childNode.replaceWith(...tempDiv.childNodes);
}
});
}
});
}
window.toggleADHDFriendlyMode = function () {
const button = document.getElementById('featureItem-adhd');
const tickIcon = document.getElementById('tickIcon-adhd');
button.classList.toggle('feature-active');
tickIcon.style.display = tickIcon.style.display === 'inline-flex' ? 'none' : 'inline-flex';
document.body.classList.toggle('adhd-friendly');
if (document.body.classList.contains('adhd-friendly')) {
saveSettings();
applyADHDFriendlyMode();
} else {
const allTextNodes = document.body.querySelectorAll(
'p:not(.uwaw *), h1:not(.uwaw *), h2:not(.uwaw *), h3:not(.uwaw *), h4:not(.uwaw *), h5:not(.uwaw *), h6:not(.uwaw *), span:not(.uwaw *), li:not(.uwaw *), a:not(.uwaw *)'
);
allTextNodes.forEach(node => {
node.innerHTML = node.innerHTML.replace(/(.*?)<\/span>/g, '$1');
});
saveSettings();
}
};
function resetSettings() {
const cookieDomain = getCookieDomain();
// Clear cookie with dynamic domain
if (cookieDomain) {
document.cookie = `${SETTINGS_KEY}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${cookieDomain}`;
} else {
// For localhost or IP addresses
document.cookie = `${SETTINGS_KEY}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;
}
speechSynthesisInstance.cancel();
const allTextNodes = document.body.querySelectorAll(
'p:not(.uwaw *), h1:not(.uwaw *), h2:not(.uwaw *), h3:not(.uwaw *), h4:not(.uwaw *), h5:not(.uwaw *), h6:not(.uwaw *), span:not(.uwaw *), li:not(.uwaw *), a:not(.uwaw *)'
);
allTextNodes.forEach(node => {
node.innerHTML = node.innerHTML.replace(/(.*?)<\/span>/g, '$1');
});
document.querySelectorAll('body *:not(.uwaw *):not(.uwaw)').forEach((el) => {
el.style.letterSpacing = "";
el.style.wordSpacing = "";
el.style.fontSize = "";
el.style.lineHeight = "";
el.style.cursor = "";
el.style.zoom = "1";
el.classList.remove("active");
});
document.body.classList.remove(
"dark-mode",
"adhd-friendly",
"custom-cursor",
"dyslexia-mode",
"highlight-links",
"hide-images"
);
document.documentElement.classList.remove("invert-colors");
fontSizeCount = 0;
lineHeightCount = 0;
textSpacingCount = 0;
screenReader = false;
saveSettings();
loadSettings();
const checkboxes = document.querySelectorAll('.uwaw input[type="checkbox"]');
checkboxes.forEach(checkbox => checkbox.checked = false);
document.querySelectorAll('.font-size-visible, .line-height-visible, .text-spacing-visible')
.forEach(el => el.classList.remove('span-visible'));
}
document.addEventListener('DOMContentLoaded', function () {
const resetBtn = document.getElementById('reset-all');
if (resetBtn) {
resetBtn.addEventListener('click', resetSettings);
}
});
function getCookie(name) {
const value = "; " + document.cookie;
const parts = value.split("; " + name + "=");
if (parts.length === 2) return decodeURIComponent(parts.pop().split(";").shift());
}
function saveSettings() {
const settings = {
screenReader: screenReader,
fontSizeCount: fontSizeCount - 1,
lineHeightCount: lineHeightCount - 1,
textSpacingCount: textSpacingCount - 1,
highlightLinks: document.body.classList.contains('highlight-links'),
dyslexiaMode: document.body.classList.contains('dyslexia-mode'),
hideImages: document.body.classList.contains('hide-images'),
darkMode: document.body.classList.contains('dark-mode'),
cursorChanged: document.body.classList.contains('custom-cursor'),
invert: document.documentElement.classList.contains('invert-colors'),
adhdFriendly: document.body.classList.contains('adhd-friendly')
};
const jsonStr = JSON.stringify(settings);
const expiryDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toUTCString(); // 30 days
const cookieDomain = getCookieDomain();
console.log(cookieDomain);
// Check if running on HTTPS
if (location.protocol === 'https:') {
if (cookieDomain) {
// For production domains with HTTPS
document.cookie = `accessibilitySettings=${encodeURIComponent(jsonStr)}; expires=${expiryDate}; path=/; domain=${cookieDomain}; SameSite=Strict; Secure`;
} else {
// For localhost/IP with HTTPS
document.cookie = `accessibilitySettings=${encodeURIComponent(jsonStr)}; expires=${expiryDate}; path=/; SameSite=Strict; Secure`;
}
} else {
// Fallback for HTTP (development)
if (cookieDomain) {
document.cookie = `accessibilitySettings=${encodeURIComponent(jsonStr)}; expires=${expiryDate}; path=/; domain=${cookieDomain}`;
} else {
document.cookie = `accessibilitySettings=${encodeURIComponent(jsonStr)}; expires=${expiryDate}; path=/`;
}
console.warn("⚠️ Secure cookie attribute skipped (not HTTPS)");
}
console.log("✅ Cookie saved for domain:", cookieDomain || 'current domain');
}
function updateWidgetToggles(settings) {
const speakOn = document.getElementById('tickIcon_sp');
if (speakOn) {
speakOn.style.display = settings.screenReader ? 'inline-flex' : 'none';
}
const highlightToggle = document.getElementById('tickIcon-ht');
if (highlightToggle) {
highlightToggle.style.display = settings.highlightLinks ? 'inline-flex' : 'none';
}
const darkModeToggle = document.getElementById('tickIcon-ht-dark');
if (darkModeToggle) {
darkModeToggle.style.display = settings.darkMode ? 'inline-flex' : 'none';
}
const invertToggle = document.getElementById('tickIcon-ic');
if (invertToggle) {
invertToggle.style.display = settings.invert ? 'inline-flex' : 'none';
}
const dyslexiaToggle = document.getElementById('tickIcon-df');
if (dyslexiaToggle) {
dyslexiaToggle.style.display = settings.dyslexiaMode ? 'inline-flex' : 'none';
}
const adhdToggle = document.getElementById('tickIcon-adhd');
if (adhdToggle) {
adhdToggle.style.display = settings.adhdFriendly ? 'inline-flex' : 'none';
}
const hideImagesToggle = document.getElementById('tickIcon-hi');
if (hideImagesToggle) {
hideImagesToggle.style.display = settings.hideImages ? 'inline-flex' : 'none';
}
const cursorToggle = document.getElementById('tickIcon-cursor');
if (cursorToggle) {
cursorToggle.style.display = settings.cursorChanged ? 'inline-flex' : 'none';
}
adjustFontSize();
adjustLineHeight();
adjustTextSpacing();
}
function loadSettings() {
let settings = getCookie(SETTINGS_KEY);
if (!settings) {
settings = getCookie(SETTINGS_KEY);
}
console.log(settings);
if (settings) {
settings = JSON.parse(settings);
fontSizeCount = settings.fontSizeCount || 0;
lineHeightCount = settings.lineHeightCount || 0;
textSpacingCount = settings.textSpacingCount || 0;
if (settings.screenReader) {
screenReader = true;
const button = document.getElementById('featureItem_sp');
button.classList.add('feature-active');
} else {
const button = document.getElementById('featureItem_sp');
button.classList.remove('feature-active');
}
if (settings.highlightLinks) {
document.body.classList.add('highlight-links');
const button = document.getElementById('featureItem-ht');
button.classList.add('feature-active');
} else {
const button = document.getElementById('featureItem-ht');
button.classList.remove('feature-active');
}
if (settings.dyslexiaMode) {
document.body.classList.add('dyslexia-mode');
const button = document.getElementById('featureItem-df');
button.classList.add('feature-active');
} else {
const button = document.getElementById('featureItem-df');
button.classList.remove('feature-active');
}
if (settings.hideImages) {
document.body.classList.add('hide-images');
const button = document.getElementById('featureItem-hi');
button.classList.add('feature-active');
} else {
const button = document.getElementById('featureItem-hi');
button.classList.remove('feature-active');
}
if (settings.darkMode) {
document.body.classList.add('dark-mode');
const button = document.getElementById('featureItem-ht-dark');
button.classList.add('feature-active');
} else {
const button = document.getElementById('featureItem-ht-dark');
button.classList.remove('feature-active');
}
if (settings.cursorChanged) {
document.body.classList.add('custom-cursor');
const button = document.getElementById('featureItem-Cursor');
button.classList.add('feature-active');
} else {
const button = document.getElementById('featureItem-Cursor');
button.classList.remove('feature-active');
}
if (settings.invert) {
document.documentElement.classList.add("invert-colors");
const button = document.getElementById('featureItem-ic');
button.classList.add('feature-active');
} else {
const button = document.getElementById('featureItem-ic');
button.classList.remove('feature-active');
}
if (settings.adhdFriendly) {
document.body.classList.add('adhd-friendly');
applyADHDFriendlyMode();
}
for (let i = 0; i < fontSizeCount; i++) {
applyTextSettings();
}
for (let i = 0; i < lineHeightCount; i++) {
updateLineHeight();
}
for (let i = 0; i < textSpacingCount; i++) {
updateLetterSpacing();
}
updateWidgetToggles(settings);
}
}
function detectRouteChange() {
const settings = JSON.parse(localStorage.getItem(SETTINGS_KEY));
const settingsStr = JSON.parse(getCookie(SETTINGS_KEY));
console.log(settings, settingsStr);
setInterval(() => {
let currentPath = window.location.pathname;
if (currentPath !== lastPath) {
speechSynthesisInstance.cancel();
lastPath = currentPath;
if (settings?.adhdFriendly || settingsStr?.adhdFriendly) {
document.body.classList.add('adhd-friendly');
applyADHDFriendlyMode();
}
for (let i = 0; i < lineHeightCount; i++) {
updateLineHeight();
}
updateLetterSpacing();
}
}, 1000);
}
})();